package com.cicdi.alayarobot.task;

import com.alaya.contracts.ppos.StakingContract;
import com.alaya.contracts.ppos.dto.resp.Reward;
import com.alaya.crypto.Credentials;
import com.alaya.crypto.WalletUtils;
import com.alaya.parameters.NetworkParameters;
import com.alaya.protocol.Web3j;
import com.alaya.utils.Convert;
import com.cicdi.alayarobot.config.AlayaProperties;
import com.cicdi.alayarobot.model.AddressReward;
import com.cicdi.alayarobot.model.DelegationListByStaking;
import com.cicdi.alayarobot.model.DelegationListByStakingInnerData;
import com.cicdi.alayarobot.model.DelegationListByStakingParam;
import com.cicdi.alayarobot.util.HttpUtil;
import com.cicdi.alayarobot.util.JsonUtil;
import com.cicdi.alayarobot.util.RewardUtil;
import com.cicdi.alayarobot.util.SendUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Scanner;
import java.util.stream.Collectors;

/**
 * @author haypo
 * @date 2020/12/7
 */
@Slf4j
@Component
public class AutoSendBonusTask {
    private static final String STAKING_DETAILS = "https://scan.alaya.network/browser-server/staking/delegationListByStaking";
    static Web3j web3j;
    static List<AddressReward> rewardList = new ArrayList<>();
    final RewardUtil rewardUtil;
    final AlayaProperties alayaProperties;
    final Credentials credentials;
    @Autowired
    SendUtil sendUtil;

    public AutoSendBonusTask(AlayaProperties alayaProperties, RewardUtil rewardUtil) throws Exception {
        //初始化钱包
        System.out.println("请输入密码:");
        String password = new Scanner(System.in).nextLine();
        System.out.println("请输入钱包路径（建议输入绝对路径）:");
        String source = new Scanner(System.in).nextLine();
        credentials = WalletUtils.loadCredentials(password, source);
        System.out.println("已加载钱包！");

        this.alayaProperties = alayaProperties;
        this.rewardUtil = rewardUtil;
        web3j = alayaProperties.getWeb3j();
        if (rewardList.isEmpty()) {
            for (String address : getAllAddressList(alayaProperties.getNodeId())) {
                rewardList.addAll(rewardUtil.getRewardList(address, alayaProperties.getNodeId()).stream().map(
                        s -> new AddressReward(address, s.getNodeId(), s.getStakingNum(), s.getReward())
                ).collect(Collectors.toList()));
            }
        }
        log.info("委托奖励列表");
        for (AddressReward addressReward : rewardList) {
            log.info(addressReward.toString());
        }


    }

    public List<String> getAllAddressList(String nodeId) {
        int page = 1;
        List<String> result = new ArrayList<>();
        try {
            while (true) {
                List<String> data = getAddressList(nodeId, page++);
                result.addAll(data);
                if (data.isEmpty()) {
                    break;
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return result;
    }

    /**
     * 获得节点的委托列表
     *
     * @param nodeId 节点id
     * @param page   页面大小
     * @return 委托列表
     * @throws Exception 一些io异常
     */
    public List<String> getAddressList(String nodeId, int page) throws Exception {
        BigInteger stakingBlockNum = StakingContract.load(web3j, NetworkParameters.CurrentNetwork.getChainId()).getStakingInfo(nodeId).send().getData().getStakingBlockNum();
        String data = JsonUtil.obj2Str(new DelegationListByStakingParam(nodeId, BigInteger.valueOf(page), alayaProperties.getPageSize(), stakingBlockNum));
        String resp = HttpUtil.postJson(AutoSendBonusTask.STAKING_DETAILS, data, "utf-8");
        DelegationListByStaking dlbs = JsonUtil.convertJsonStringToObject(resp, DelegationListByStaking.class);
        return Objects.requireNonNull(dlbs).getData().stream()
                .map(DelegationListByStakingInnerData::getDelegateAddr).collect(Collectors.toList());
    }

    /**
     * 延迟1s启动，每60s执行一次
     */
    @Scheduled(initialDelay = 1000, fixedDelay = 1000 * 60)
    public void sendBonus() {
        try {
            System.out.println("----------------------------------------");
            List<AddressReward> currentRewardList = new ArrayList<>();
            if (rewardList.isEmpty()) {
                log.info("暂无委托奖励！");
                return;
            }
            for (String address : getAllAddressList(alayaProperties.getNodeId())) {
                List<Reward> rewardList = rewardUtil.getRewardList(address, alayaProperties.getNodeId());
                currentRewardList.addAll(
                        rewardList.stream().map(
                                s -> new AddressReward(address, s.getNodeId(), s.getStakingNum(), s.getReward()
                                )).collect(Collectors.toList()));
            }
            log.info("委托奖励列表");
            for (AddressReward addressReward : currentRewardList) {
                log.info(addressReward.toString());
            }

            for (AddressReward reward : rewardList) {
                if (currentRewardList.contains(reward) &&
                        reward.getReward().doubleValue() > 0 &&
                        currentRewardList.get(currentRewardList.indexOf(reward)).getReward().doubleValue() == 0) {
                    if (alayaProperties.getBlackList().contains(reward.getAddress())) {
                        continue;
                    }
                    BigDecimal bonusVon = new BigDecimal(reward.getReward()).multiply(alayaProperties.getBonusPer());
                    BigDecimal bonusAtp = Convert.fromVon(bonusVon, Convert.Unit.ATP);
                    log.info("{}已经领取奖励", reward.getAddress());
                    sendUtil.fastSend(credentials, reward.getAddress(), bonusAtp);
                    log.info("发送 {} atp给{}", bonusAtp, reward.getAddress());
                } else {
                    log.info("{}暂未领取奖励，无需发送奖励", reward.getAddress());
                }
            }
            rewardList.clear();
            rewardList.addAll(currentRewardList);
        } catch (Exception e) {
            log.info("暂无委托奖励！");
            log.error(e.getMessage(), e);
        }
    }
}
